Skip to content

feat(desktop): discover and spawn ACP harnesses installed in WSL on Windows - #3679

Open
spfcraze wants to merge 1 commit into
block:mainfrom
spfcraze:feat/windows-wsl-harness-fallback
Open

feat(desktop): discover and spawn ACP harnesses installed in WSL on Windows#3679
spfcraze wants to merge 1 commit into
block:mainfrom
spfcraze:feat/windows-wsl-harness-fallback

Conversation

@spfcraze

@spfcraze spfcraze commented Jul 30, 2026

Copy link
Copy Markdown

Problem

On Windows, Buzz Desktop discovers ACP harnesses by resolving their command
on the Windows PATH. Several supported harnesses are commonly installed
inside WSL, not on the Windows host — Hermes Agent (hermes-acp) is the
canonical case: it is a Linux-first install, so on a Windows machine running
Hermes in WSL, Doctor reports Not installed and the harness cannot be
started, even though it works perfectly inside the distro.

There was no WSL story anywhere in the harness discovery/spawn path (the only
WSL awareness in the tree is is_windows_apps_alias, which exists to avoid
WindowsApps stub launchers, issue #2328).

What this PR does

Buzz now falls back to the default WSL distribution when a preset or
custom harness command is missing from the Windows PATH:

  1. Discovery (managed_agents/wsl.rs, new) — probes the default distro
    via wsl.exe -e sh -c …, checking ~/.local/bin, ~/bin,
    /usr/local/bin, then the distro PATH (command -v). Positive results
    mark the harness Available with a wsl://… binary path in Doctor.
    Resolutions are cached for the app lifetime (mirrors resolve_command's
    cache contract). Only bare command names are ever probed — paths are
    rejected up front (no injection surface, no nonsense lookups).

  2. Spawn contract (runtime.rsbuzz-acp) — when the host PATH
    resolution failed but the WSL probe succeeded, Desktop sets
    BUZZ_ACP_AGENT_WSL_PATH (absolute in-distro path) and optionally
    BUZZ_ACP_AGENT_WSL_DISTRO on the buzz-acp child.
    BUZZ_ACP_AGENT_COMMAND keeps the original bare identity
    (hermes-acp), so all per-runtime defaults keyed on the command name
    (default_agent_env, default_agent_args) apply unchanged.

  3. Spawn wrapping (buzz-acp/src/acp.rs) — AcpClient::spawn reads the
    contract and launches wsl.exe [-d distro] -e <linux_path> <args> instead
    of the bare command. WSL interop bridges the stdio pipes; the ACP/NDJSON
    channel is byte-transparent. Contract values are validated (absolute POSIX
    path required); a set contract on a non-Windows host is ignored with a
    warning.

  4. Environment forwarding — wsl.exe children only import Windows-side
    variables listed in WSLENV. buzz-acp builds the forward list from
    every agent-intended key (per-runtime defaults like
    HERMES_ACP_SKIP_CONFIGURED_MCP=1, persona env like GOOSE_PROVIDER,
    merged CODEX_CONFIG), merging into any pre-existing WSLENV without
    duplicating flagged entries (GOPATH/p style). Without this, agents in
    WSL silently lose their env — for Hermes specifically this re-introduces
    the [Bug] Hermes model discovery times out in Buzz Desktop v0.5.0 #3355 startup-stall the skip flag exists to prevent.

Verification (real, on a Windows 11 + WSL2 machine)

Environment: Windows 11, WSL2 Ubuntu, Hermes Agent v0.19.0 at
/home/userspace/.local/bin/hermes-acp, Buzz Desktop installed at
C:\Users\user1\AppData\Local\Buzz\.

  1. Baseline bug: hermes-acp absent from Windows PATH → Doctor shows
    Hermes Not installed.
  2. Stdio transparency: spawning
    C:\Windows\System32\wsl.exe -e /home/userspace/.local/bin/hermes-acp from a
    Windows parent with piped stdio returns a byte-clean ACP initialize
    response (agentInfo: hermes-agent 0.19.0, protocolVersion: 1).
  3. Env forwarding: without WSLENV, hermes-acp hangs before
    initialize (the [Bug] Hermes model discovery times out in Buzz Desktop v0.5.0 #3355 MCP-startup stall). With
    HERMES_ACP_SKIP_CONFIGURED_MCP=1 set Windows-side and forwarded via
    WSLENV, initialize answers immediately.
  4. Zero-config workaround validated: the manual custom-harness JSON
    documented in docs/windows-wsl-harnesses.md (same mechanism this PR
    automates) loads and works on the current released build.

Tests

  • buzz-acp: 8 new unit tests (build_wsl_wrap validation, prefix_args,
    merge_wslenv order/dup/flag handling). cargo test -p buzz-acp: 648
    passed, 0 failed
    (full suite, Linux). cargo clippy -p buzz-acp --all-targets: clean.
  • Desktop wsl.rs: 6 unit tests (probe script contract pins ~/.local/bin
    before PATH, no login shell; stdout parsing; display path;
    probeable-command validation incl. metacharacter rejection; negative-result
    caching) — pass on Linux. Process-spawning probes are cfg(windows)-gated;
    the Windows-only code paths (resolve_wsl_exe, probe spawn) were
    compile-verified for x86_64-pc-windows-msvc.
  • cargo check (Linux) on desktop/src-tauri: green, zero warnings.
    cargo test managed_agents:: on desktop/src-tauri: 906 passed, 0
    failed
    (includes the 6 new wsl.rs tests + all pre-existing discovery,
    custom-harness, spawn-hash, and readiness tests).
  • Full x86_64-pc-windows-msvc workspace check was not runnable in the dev
    container (crypto -sys crates need a Windows C toolchain); Windows CI
    covers it. Every Windows-gated line added by this PR is either
    std-only Rust (verified via the standalone msvc-target compile described
    above) or mirrors existing patterns in git_bash.rs.
  • Existing discovery tests bind the resolver seam (preset_catalog_entry) —
    unchanged, still green.

Notes for reviewers

  • Scope: preset + custom harnesses only. Tier-1 builtins (auth probes,
    install scripts) still assume host-native installs — a follow-up could
    extend the same cached-resolution mechanism to their probes.
  • Perf: probes run once per missing command per app launch, cache-first
    on both the discovery and spawn paths. WSL-unavailable machines
    short-circuit on wsl.exe resolution.
  • Future: WslCommandResolution.distro is plumbed through the whole
    contract but always None today (default distro only) — multi-distro
    discovery needs no contract change.
  • New public API: none (all pub(crate) / private). Docs added:
    docs/windows-wsl-harnesses.md.

@spfcraze
spfcraze requested a review from a team as a code owner July 30, 2026 03:56
…indows

When a preset or custom harness command is missing from the Windows PATH,
harness discovery now probes the default WSL distribution (~/.local/bin,
~/bin, /usr/local/bin, then the distro PATH) and marks the harness
Available with a wsl:// binary path. At spawn, Desktop hands the in-distro
path to buzz-acp (BUZZ_ACP_AGENT_WSL_PATH, optional
BUZZ_ACP_AGENT_WSL_DISTRO), which wraps the agent launch through
wsl.exe -e and forwards all agent-intended env vars across the boundary
via a computed WSLENV merge. BUZZ_ACP_AGENT_COMMAND keeps the bare command
identity so per-runtime defaults (e.g. HERMES_ACP_SKIP_CONFIGURED_MCP)
apply unchanged.

This unblocks running Hermes Agent (Linux-first install, hermes-acp) from
Buzz Desktop on Windows without a hand-authored custom harness.

Docs: docs/windows-wsl-harnesses.md
Signed-off-by: Frank Saunders <frank@users.noreply.github.com>
@spfcraze
spfcraze force-pushed the feat/windows-wsl-harness-fallback branch from d2afdfa to 0458c49 Compare July 30, 2026 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant